<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
<div id="app">
	<h1>我是{{title}}{{title}}</h1>
	<h2>{{msg}}</h2>
	<div>
		<h3 v-if="isShow">这玩意儿有时候会消失</h3>
		<h3>接下来的内容为</h3>
		<h3>{{title}}</h3>
		<h3 v-if="isShow">这玩意儿有时候会消失</h3>
	</div>
	<ul>
		<li v-for="user in users">{{user.name}}</li>
	</ul>
</div>
<script>
	var toString2 = {}.toString;
	var model = {
		"arr": [1, 2, 3, 4, 5],
		"obj": {msg: "wiz"},
		"title": "This is title;",
		"msg": "This is msg;",
		"isShow": true,
		"users": [{"name": "wiz111"}, {"name": "wiz33332"}, {"name": "wiz888883"}],
	}
	var observerFactory = function(model) {
		for(var property in model) {
			addObserver(model, property, model[property]);
			if(toString2.call(model[property]) === "[object Object]") {
				observerFactory(model[property]);
			}
			if(toString2.call(model[property]) === "[object Array]") {
				for(var i = 0; i < model[property].length; i++) {
					if(toString2.call(model[property][i]) === "[object Object]") {
						observerFactory(model[property][i]);
					}
				}
				["concat", "join", "filter", "pop", "push", "shift", "slice", "splice", "unshift"].forEach(function(method) {
					var _o_ = Array.prototype[method];
					model[property][method] = function(arguments) {
						var res = _o_.call(this, arguments);
						notify(model, property, this);
						return res;
					}
				});
			}
		}
	}
	var addObserver = function(obj, property, val) {
		Object.defineProperty(obj, property, {
			enumerable: true,
			configurable: false,
			set: function(nval) {
				notify(obj, property, nval);
				val = nval;
			},
			get: function() {
				return val;
			}
		})
	}
	var elArr = [];
	var notify = function(obj, property, val) {
		console.log("notify");
		if(bind[property]) {
			bind[property].forEach(function(node) {
				console.log(node.cmdType);
				switch(node.cmdType) {
					case "text":
						var ndata;
						var regexp = /(\{\{\s*(\w*)\s*\}\})/g;
						while(regexp.exec(node.data)) {
							ndata = node.data.replace(RegExp.$1, val);
							regexp.lastIndex = regexp.lastIndex - RegExp.$1.length + String(val).length;
						}
						node.data = ndata;
						break;
					case "show":
						node.style["display"] = (val ?  "block" : "none");
						break;
					case "if":
						if(val) {
							node.ref.parentNode.ref.insertBefore(node, node.ref.nextSibling.ref);
						} else {
							node.remove();
						}
						break;
					case "for":
						node.remove();
						node.ref.parentElement.ref.innerHTML = "";
						console.log(node.ref.parentElement.ref);
						for(var i = 0; i < val.length; i++) {
							var clone = node.cloneNode(true);
							var html = clone.innerHTML;
							while(res = /(\{\{\s*(\w+)\.(\w+)\s*\}\})/g.exec(html)) {
								html = html.replace(RegExp.$1, val[i][RegExp.$3]);
							}
							clone.innerHTML = html;
							node.ref.parentElement.ref.appendChild(clone);
						}
						break;
					default:
						break;
				}
			});
		}
	}
	var bind = {}
	var mvvm = function(node) {
		var clone = node.cloneNode(false);
		node.ref = clone;
		clone.ref = node;
		for(var i = 0; i < node.childNodes.length; i++) {
			if(node.childNodes[i]) {
				clone.appendChild(mvvm(node.childNodes[i]));
			}
		}
		switch(node.nodeType) {
			case 1:
				parseElement(node.ref);
				break;
			case 3:
				var regexp = /(\{\{\s*(\w*)\s*\}\})/g;
				while(regexp.exec(node.data)) {
					bind[RegExp.$2]  = bind[RegExp.$2] || [];
					bind[RegExp.$2].push(node.ref);
					node.ref.cmdType = "text";
				}
				break;
			default:
				break;
		}
		return clone;
	}
	var parseElement = function(node) {
		if(node.hasAttribute("v-show")) {
			var m = node.getAttribute("v-show");
			bind[m] = bind[m] || [];
			bind[m].push(node);
			node.cmdType = "show";
		} else if(node.hasAttribute("v-if")) {
			var m = node.getAttribute("v-if");
			bind[m] = bind[m] || [];
			bind[m].push(node);
			node.cmdType = "if";
		} else if(node.hasAttribute("v-for")) {
			var m = node.getAttribute("v-for");
			/\s*(\w+)\s+in\s+(\w+)\s*/g.exec(m);
			bind[RegExp.$2] = bind[RegExp.$2] || [];
			bind[RegExp.$2].push(node);
			/(\{\{\s*(\w+)\.(\w+)\s*\}\})/g.exec(node.innerHTML);
			bind[RegExp.$3] = bind[RegExp.$3] || [];
			bind[RegExp.$3].push(node);
			node.cmdType = "for";
		}
	}
	function init(model) {
		for(var property in model) {
			model[property] = model[property];
		}
	}
	observerFactory(model);
	var element = document.querySelector("#app");
	var parent = element.parentNode;
	element.remove();
	var clone = mvvm(element);
	var clone2;
	parent.appendChild(clone);
	init(model);
	function test(c) {
		clone2 = c;
		c = clone2;
	}
	test(clone);
</script>
</body>
</html>